package Controller;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Stack;
import Model.Mix;
import Model.Mixes;
import Model.TokenManager;
import View.Screen;

public class Sender 
{
	private final byte[] DUMMY_PUSH = {1};
	
	private int index;
	private int currentIndex;
	
	private TokenManager m_Manager;

	private Mixes m_Mixes;
	
	@SuppressWarnings("unused")
	private Screen m_Screen;
	
	public Sender()
	{
		index = 0;
		currentIndex = index;
		
		m_Manager = new TokenManager();
		
		setPublicParams();
		
		m_Mixes = new Mixes();
		
		m_Screen = new Screen(this);	
		
		
	}
	
	private byte[] send(byte[] msg, String host, Integer port) {
		try {
	         Socket skt = new Socket(host, port);
	         DataInputStream in = new DataInputStream(skt.getInputStream());	         

	         DataOutputStream out = new DataOutputStream(skt.getOutputStream());

	         byte[] b = msg;
	         
	         out.writeInt(b.length); // write length of the message
	         out.write(b);   
	         
	      // get client's request
			byte[] message = null;
			int length = in.readInt();                    // read length of incoming message
			if(length>0) {
			    message = new byte[length];
			    in.readFully(message, 0, message.length); // read the message
			}

	         out.close();
	         in.close();
	         skt.close();
	         
	         boolean isMessageExists = message.length > 0;
	         if (isMessageExists)
	         {
	        	 return message;
	         }	         
	      }
	      catch(Exception e) 
	      {
	    	  System.out.println(e.toString());
	      }
		
		return null;
	}
	
	private byte[] send(byte[] msg)
	{
		return send(msg, "localhost", 1234);
	}
	
	public void setPublicParams()
	{			
		byte[] response = send(m_Manager.getPubliParamsMessage());
		try {
			m_Manager.setPublicParameters(response);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	

	

	private byte[] buildEncryptedMessage(byte[] b, Stack<Mix> mixes, String poAddress, int poPort) 
	{
		byte[] msg = b;
		
		Mix prev = mixes.pop();
		
		msg = prev.encrypt(msg, poAddress, poPort);
		
		while (!mixes.isEmpty())
		{
			Mix mix = mixes.pop();
			
			msg = mix.encrypt(msg, prev.getAddress(), prev.getPort());
			
			prev = mix;
		}
		
		return msg;
	}
	
	private byte[] buildEncryptedMessage(byte[] b, Stack<Mix> mixes) 
	{
		byte[] msg = b;
		
		Mix prev = mixes.pop();
		
		msg = prev.encrypt(msg);
		
		while (!mixes.isEmpty())
		{
			Mix mix = mixes.pop();
			
			msg = mix.encrypt(msg);
			
			prev = mix;
		}
		
		return msg;
	}

	
	
	public String pushPull(byte[] pushToken, String msg, byte[] pullToken)
	{
		Stack<Mix> pathBack = m_Mixes.get(3,2,1,0);
		//Stack<Mix> pathBack = m_Mixes.get(0);
		
		byte[] bMessage = DUMMY_PUSH;
				
		if (!msg.isEmpty())
		{
			bMessage = buildEncryptedMessage(msg.getBytes(), pathBack);
		}
		
		byte[] bPush = m_Manager.getPushMessage(bMessage, pushToken);
		byte[] bPull = m_Manager.getPullMessage(pullToken, m_Manager.getProof(), currentIndex);				
		
		//currentIndex = -1;
		
		byte[] b = m_Manager.arraysConcatenation(bPull, bPush);
		
		Stack<Mix> path = m_Mixes.get(0,1,2,3);
		//Stack<Mix> path = m_Mixes.get(0);
		
		byte[] message = buildEncryptedMessage(b, path, "localhost", 1234);
		
		Mix firstMix = m_Mixes.get(0).get(0);
		
		byte[] response = send(message, firstMix.getAddress(), firstMix.getPort());
		
		String res = "";
		
		if (response != null && response.length > 1)
		{
			res = new String(response);
			index++;
			currentIndex = index;
		}				
		
		return res;
	}

	public String getMyPublicToken() 
	{
		return m_Manager.getMyTokenString();
	}
}
